import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Random;

import javax.microedition.rms.RecordStore;

public class MyRMS {

	private static final int BUFFER_SIZE = 124;
	private static final String phone_2_linux_ResourceName = "mmphone";
	private static final String linux_2_phone_ResourceName = "mmlinux";
	private static final String linux_2_phone_ControlName = "mmcontrol";

	private static final String cmd_prefix = "sg";
	private static final byte cmd_null = '*';

	static byte record_buffer[] = new byte[BUFFER_SIZE];

	static byte last_seen_alternate_char = cmd_null;
	final static char prog_mark = 101;

	static final int alternate_char_offset = 50;
	static long send_message_count = 0;
	static byte snd_msg_alternate_char = (byte) (send_message_count + alternate_char_offset);
	/* message format: prefix+alternat_char+length+msg */
	private static String[] binary_map = { "0001", "0010", "0011", "0100",
			"0101", "0110", "0111", "1000", "1001" };

	static boolean read_control_successful = false;

	public static void reset_record() {
		byte control_buffer[] = new byte[1024];

		byte cmd_prefix_bytes[] = cmd_prefix.getBytes();
		// initial set of the record
		for (int i = 0; i < BUFFER_SIZE; i++)
			record_buffer[i] = cmd_null;

		for (int i = 0; i < 1024; i++)
			control_buffer[i] = cmd_null;

		for (int i = 0; i < cmd_prefix.length(); i++) {
			record_buffer[i] = cmd_prefix_bytes[i];
			control_buffer[i] = cmd_prefix_bytes[i];
		}

		rms_save_bytes(record_buffer, phone_2_linux_ResourceName);
		rms_save_bytes(record_buffer, linux_2_phone_ResourceName);

		if (!read_control_successful
				&& rms_load_string(linux_2_phone_ControlName) == null)
			rms_save_bytes(control_buffer, linux_2_phone_ControlName); // initialize
	}

	public static void set_app_channel(byte ch) {
		/*
		 * ch: 0 -> do not forward any message to me 1 -> forward message with
		 * header (e.g. chat) only 2 -> forward message without header (e.g.
		 * elevator) only 255 -> forward all message
		 */

		byte control_msg[] = new byte[3];
		control_msg[0] = (byte) 0xff; // phone_id: for control msg, it does not
										// matter
		control_msg[1] = 0; // indicate it's a control message
		control_msg[2] = ch;
		send_cmd(control_msg);
	}

	public static String padZeros(String s) {
		while (s.length() < 8)
			s = "0" + s;
		return s;
	}

	public static String XOR(String s1, String s2) {
		String ret = "";
		for (int i = 0; i < s1.length(); i++) {
			if (s1.charAt(i) == s2.charAt(i))
				ret += "0";
			else
				ret += "1";
		}
		return ret;
	}

	public static String encrypt_cmd(int indx, int num_moves) {
		Random r = new Random();
		String binary = binary_map[indx - 1], ret = "";
		String[] msg_binary = new String[4], mac_binary = new String[4];
		//System.out.println("Encrypting " + indx + ", " + num_moves + ": ");
		//System.out.println(" cipher: ");
		for (int i = 0; i < 4; i++) {
			char digit = binary.charAt(i);
			int rand = r.nextInt(128);
			if (digit == '1')
				rand = rand + 128;

			msg_binary[i] = padZeros(Integer.toBinaryString(rand));
			//System.out.println(" " + rand + " " + msg_binary[i]);
		}

		for (int i = 0; i < 4; i++) {
			int mult = 255 / (num_moves + i + 1);
			String tmp = padZeros(Integer.toBinaryString(mult));
			mac_binary[i] = XOR(msg_binary[i], tmp);
		}

		for (int i = 0; i < 4; i++) {
			ret += msg_binary[i] + mac_binary[i];
		}

		return ret;
	}

	public static int decrypt_cmd(String cmd, int num_moves) {
		String[] msg_binary = new String[4], mac_binary = new String[4];
		int final_indx = -1;
		String indx_binary = "";
		if (cmd.length() != 64)
			return -1;
		
		for (int i=0; i<4; i++) {
			msg_binary[i] = cmd.substring(i*16, i*16+8);
			mac_binary[i] = cmd.substring(i*16+8, i*16+16);
			
			// check mac
			int mult = 255 / (num_moves + i + 1);
			String tmp = padZeros(Integer.toBinaryString(mult));
			if (!XOR(msg_binary[i], tmp).equals(mac_binary[i]))
				return -1;
			
			int rand = Integer.parseInt(msg_binary[i], 2);
			//System.out.println(msg_binary[i] + " " + rand);
			if (rand > 127)
				indx_binary += "1";
			else indx_binary += "0";
		}
		
		final_indx = Integer.parseInt(indx_binary, 2);
		if (final_indx > 9 || final_indx < 1)
			return -1;
		
		return final_indx;
	}

	public static void send_cmd(byte cmd[]) {
		// send a command
		int start_pos = cmd_prefix.length();
		int msg_size = cmd.length;

		// add alternate char
		record_buffer[start_pos] = snd_msg_alternate_char;
		start_pos++;

		if (msg_size > BUFFER_SIZE - start_pos - 1)
			msg_size = BUFFER_SIZE - start_pos - 1;

		record_buffer[start_pos] = (byte) msg_size;

		for (int i = 0; i < msg_size; i++)
			record_buffer[start_pos + i + 1] = cmd[i];

		int r = rms_save_bytes(record_buffer, phone_2_linux_ResourceName);

		int tries = 2;

		while (r < 0 && tries-- > 0) {
			try {
				Thread.sleep(10);
				// r=rms_save_string(t, phone_2_linux_ResourceName);
				r = rms_save_bytes(record_buffer, phone_2_linux_ResourceName);
			} catch (Exception e) {
			}
		}

		if (r > 0) {
			snd_msg_alternate_char = (byte) (send_message_count % 2 + alternate_char_offset);
		}
		System.out.println(record_buffer + "  " + new String(record_buffer));
	}

	public static final byte[] get_cmd() {
		byte[] rs = rms_load_bytes(linux_2_phone_ResourceName);
		int tries = 2;
		while (rs == null && tries-- > 0) {
			try {
				Thread.sleep(10);
				rs = rms_load_bytes(linux_2_phone_ResourceName);
			} catch (Exception e) {
			}
		}

		if (rs == null)
			return null;

		int pre_len = cmd_prefix.length();

		if (rs[pre_len] == last_seen_alternate_char)
			return null;

		// check message
		int msg_len = rs[pre_len + 1];
		if (msg_len < 1 || msg_len > rs.length - pre_len - 2)
			return null;

		last_seen_alternate_char = rs[pre_len];

		byte ret[] = new byte[msg_len];

		for (int i = 0; i < msg_len; i++)
			ret[i] = rs[pre_len + 2 + i];

		return ret;
	}

	public static final String get_control_info() {
		// get a linux command
		byte rs[] = rms_load_bytes(linux_2_phone_ControlName);
		System.out.println(rs);
		int tries = 2;
		while (rs == null && tries-- > 0) {
			try {
				System.out.println(rs);
				Thread.sleep(10);
				rs = rms_load_bytes(linux_2_phone_ControlName);
			} catch (Exception e) {
			}
		}
		if (rs == null)
			return null;

		int pre_len = cmd_prefix.length();

		if (rs[pre_len] == cmd_null)
			return null;

		// check message
		int msg_len = rs[pre_len + 1];
		if (msg_len < 1 || msg_len > rs.length - pre_len - 2) // too small or
																// too big
			return null;

		byte[] r = new byte[msg_len];
		for (int i = 0; i < msg_len; i++)
			r[i] = rs[pre_len + 2 + i];

		read_control_successful = true;

		return new String(r);
	}

	public MyRMS() {
	}

	public static final String rms_loadres(String filename) {
		// load a string from the jar file
		try {
			InputStream is = TicTacToe.class.getResourceAsStream(filename);
			if (is == null)
				return null;

			StringBuffer str = new StringBuffer();
			byte b[] = new byte[1];
			while (is.read(b) != -1) {
				str.append(new String(b));
			}
			is.close();
			return str.toString();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}

	public static final void rms_delete(String delete) {
		// deletes rms file. input string is filename
		try {
			RecordStore.deleteRecordStore(delete);
		} catch (Exception e) {
			// Do some exception handling here
		}
	}

	public static final String[] rms_list() {
		// returns a list of all rms files
		try {
			String[] output = RecordStore.listRecordStores();
			return output;
		} catch (Exception e) {
			return null;
		}
	}

	public static final int rms_save_bytes(byte[] data, String filename) {
		RecordStore SettingStore;

		try {
			SettingStore = RecordStore.openRecordStore(filename, true);
			if (SettingStore == null)
				return -1;

			if (SettingStore.getNumRecords() == 0)
				SettingStore.addRecord(data, 0, data.length);
			else
				SettingStore.setRecord(1, data, 0, data.length);

			SettingStore.closeRecordStore();
		} catch (Exception e) {
			return -1;
		}
		return 1;
	}

	public static byte[] rms_load_bytes(String filename) {
		RecordStore SettingStore;
		try {
			SettingStore = RecordStore.openRecordStore(filename, false);
			if (SettingStore == null)
				return null;

			byte[] data = SettingStore.getRecord(1);
			SettingStore.closeRecordStore();
			return data;
		} catch (Exception e) {
			return null;
		}
	}

	public static final int rms_save_string(String input, String filename) {
		RecordStore SettingStore;
		ByteArrayOutputStream baos;
		DataOutputStream dos;
		byte[] data;

		try {
			SettingStore = RecordStore.openRecordStore(filename, true);
			if (SettingStore == null)
				return -1;

			baos = new ByteArrayOutputStream();
			dos = new DataOutputStream(baos);

			dos.writeUTF(input);

			data = baos.toByteArray();
			if (SettingStore.getNumRecords() == 0)
				SettingStore.addRecord(data, 0, data.length);
			else
				SettingStore.setRecord(1, data, 0, data.length);
			SettingStore.closeRecordStore();

			dos.close();
			baos.close();
		} catch (Exception e) {
			return -1;
		}
		return 1;
	}

	public static final String rms_load_string(String filename) {

		RecordStore SettingStore;
		ByteArrayInputStream bais;
		DataInputStream dis;
		byte[] data;
		String output;

		try {
			SettingStore = RecordStore.openRecordStore(filename, false);
			if (SettingStore == null)
				return null;

			data = SettingStore.getRecord(1);
			bais = new ByteArrayInputStream(data);
			dis = new DataInputStream(bais);

			output = dis.readUTF();
			dis.close();
			bais.close();
			SettingStore.closeRecordStore();
			return output;
		} catch (Exception e) {
			return null;
		}
	}
}
